package com.example.demo.validator;
import com.example.demo.common.exception.BusinessException;
import com.example.demo.util.DateUtil;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import javax.validation.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 自定义日期校验
 * @Date: 2020/3/30 16:07
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateTime.DateTimeHandle.class)
public @interface DateTime {


    // 提示内容
    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * 自定义验证处理类
     */
    class DateTimeHandle implements ConstraintValidator<DateTime, Object> {
        private DateTime dateTime;

        @Override
        public void initialize(DateTime dateTime) {
            this.dateTime = dateTime;
        }

        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            Class<?> valueClass = value.getClass();
            List<Field> fields = getDateTimeAnnotationField(valueClass);
            BeanWrapper beanWrapper = new BeanWrapperImpl(value);
            for (Field field : fields) {
                field.setAccessible(true);
                //不为空的验证，为空的使用@NotBlank @NotNull等进行验证
                //当前属性的值
                String currentDateValue = (String) beanWrapper.getPropertyValue(field.getName());
                if (StringUtils.isNotEmpty(currentDateValue)) {
                    DateTimeValidator dateTimeValidator = field.getAnnotation(DateTimeValidator.class);
                    String format = dateTimeValidator.format();
                    // 获取注解DateTimeValidator中before的值
                    String beforeField = dateTimeValidator.before();
                    // 获取注解DateTimeValidator中after的值
                    String afterField = dateTimeValidator.after();
                    boolean identicalBefore = dateTimeValidator.identicalBefore();
                    boolean identicalAfter = dateTimeValidator.identicalAfter();
                    // 之前时间 以后时间的值
                    Object beforeDateValue, afterDateValue;

                    // 验证时间格式
                    if (StringUtils.isNotEmpty(format) && !dateFormatValidator(currentDateValue, format)) {
                        String apiModelProperty = field.getAnnotation(ApiModelProperty.class).value();
                        if(StringUtils.isBlank(apiModelProperty)) apiModelProperty = field.getName();
                        throw new BusinessException(500, apiModelProperty + "格式错误，应为" + format);
                    }

                    // 验证前面时间是否成立
                    if (StringUtils.isNotEmpty(beforeField)) {
                        beforeDateValue = beanWrapper.getPropertyValue(beforeField);
                        if (!compareTime(beforeDateValue, currentDateValue, identicalBefore, format)) {
                            throw new BusinessException(500, dateTimeValidator.beforemessage());
                        }
                    }

                    // 验证后面时间是否成立
                    if (StringUtils.isNotEmpty(afterField)) {
                        afterDateValue = beanWrapper.getPropertyValue(afterField);
                        if (!compareTime(currentDateValue, afterDateValue, identicalAfter, format)) {
                            throw new ValidationException(dateTimeValidator.aftermessage());
                        }
                    }
                }
            }
            return true;
        }

        /**
         * 比较两个时间，afterTime是否在beforeTime之后
         *
         * @param beforeTime 较早时间
         * @param afterTime  较晚时间
         * @param identical  是否允许相等
         * @param format     ******时间格式，这里约定两个时间格式相同 ，如果时间格式不同需要分别取出两个时间的时间格式传进来再比较
         * @return 是否成立
         */
        private static boolean compareTime(Object beforeTime, Object afterTime, boolean identical, String format) {

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
            try {
                if(Objects.isNull(beforeTime) || StringUtils.isBlank(beforeTime.toString())) beforeTime = DateUtil.getCurrentDateStr();
                if(Objects.isNull(afterTime) || StringUtils.isBlank(afterTime.toString())) afterTime = DateUtil.getCurrentDateStr();
                Date beforDate = simpleDateFormat.parse(beforeTime.toString());
                Date afterDate = simpleDateFormat.parse(afterTime.toString());
                int i = afterDate.compareTo(beforDate);
                switch (i) {
                    case 0:
                        return identical;
                    case 1:
                        return true;
                    case -1:
                        return false;
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return false;
        }


        /**
         * 时间格式校验
         *
         * @param currentDateValue 验证的时间
         * @param format           时间格式
         * @return 是否成立
         */
        private boolean dateFormatValidator(Object currentDateValue, String format) {
            if (currentDateValue.toString().length() != format.length()) {
                return false;
            }
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
            try {
                simpleDateFormat.parse(currentDateValue.toString());
            } catch (Exception e) {
                return false;
            }
            return true;
        }

        /**
         * 获取有时间校验的字段
         *
         * @param valueClass 获取字段提供的类
         * @return 是否成立
         */
        private List<Field> getDateTimeAnnotationField(Class<?> valueClass) {
            Field[] fields = valueClass.getDeclaredFields();
            if (Objects.nonNull(fields)) {
                return Arrays.stream(fields).filter(fls -> Objects.nonNull(fls.getAnnotation(DateTimeValidator.class))).collect(Collectors.toList());
            }
            return null;
        }
    }
}
